package cn.jbolt.ai.resource.function.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.jbolt.ai.app.service.AiAppConfigService;
import cn.jbolt.ai.core.chain.ChainContext;
import cn.jbolt.ai.core.func.NativeFunctionCall;
import cn.jbolt.ai.resource.function.enums.SyncStrategy;
import cn.jbolt.ai.resource.function.service.AiFunctionParamService;
import cn.jbolt.util.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.jbolt.ai.resource.function.dao.AiFunctionResourceMapper;
import cn.jbolt.ai.resource.function.entity.AiFunctionResource;
import cn.jbolt.ai.resource.function.entity.AiFunctionParam;
import cn.jbolt.ai.resource.function.service.AiFunctionResourceService;
import com.jboltai.capability.functioncall.FunctionParam;
import com.jboltai.event.ai.aifunccall.AIFuncCallEvent;
import com.jboltai.resource.ResourceCenter;
import com.jboltai.resource.functioncall.FunctionResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *
 * </p>
 *
 * @author
 * @since 2023-03-29
 */
@Service
public class AiFunctionResourceServiceImpl extends ServiceImpl<AiFunctionResourceMapper, AiFunctionResource> implements AiFunctionResourceService {

    private static final Logger logger = LoggerFactory.getLogger(AiFunctionResourceServiceImpl.class);

    @Autowired
    private ResourcePatternResolver resourcePatternResolver;
    @Autowired
    private AiFunctionParamService aiFunctionParamService;
    @Autowired
    private AiAppConfigService aiAppConfigService;

    @Override
    public Result<AiFunctionResource> getFunctionById(Long id) {
        AiFunctionResource function = this.getById(id);
        if (function != null) {
            return Result.success(function);
        } else {
            return Result.error("AI函数不存在");
        }
    }

    @Override
    public Result listByPage(Integer pageNumber, Integer pageSize, String keywords, Integer type, Boolean enable) {
        QueryWrapper<AiFunctionResource> queryWrapper = new QueryWrapper<>();
        if (StrUtil.isNotBlank(keywords)) {
            queryWrapper.and(wrapper -> wrapper.like("sn", keywords)
                    .or()
                    .like("brief_info", keywords).or()
                    .like("service_class", keywords).or()
                    .like("service_method", keywords));
        }
        if (type != null) {
            queryWrapper.eq("type", type);
        }
        if (enable != null) {
            queryWrapper.eq("enable", enable);
        }
        queryWrapper.orderByDesc("id");
        List<AiFunctionResource> functionList = new ArrayList<>();
        if (pageNumber != null && pageSize != null) {
            Page<AiFunctionResource> pager = this.page(new Page<>(pageNumber, pageSize), queryWrapper);
            functionList = pager.getRecords();
            functionList.stream().map(aiFunction -> {
                String s = aiFunction.getServiceClass() + ":" + aiFunction.getServiceMethod();
                aiFunction.setServicePackageClassMethod(s);
                return aiFunction;
            }).collect(Collectors.toList());
            pager.setRecords(functionList);
            return Result.success(Pager.from(pager));
        } else {
            functionList = this.list(queryWrapper);
            return Result.success(functionList);
        }

    }

    @Override
    public Result add(AiFunctionResource function) {
        Result result = getFunctionBySn(function.getSn());
        if (result.isError()) {
            return result;
        }
        function.setParamCount(0);
        function.setEnable(false);
        function.setType("Native");//现在都是本地的
        if (this.save(function)) {
            return Result.success();
        } else {
            return Result.error("添加失败");
        }
    }

    /**
     * 在添加之前,要先判断sn之前是否在数据库中存在
     *
     * @param sn
     * @return
     */
    public Result getFunctionBySn(String sn) {

        if (this.getOne(new QueryWrapper<AiFunctionResource>().eq("sn", sn)) != null) {
            return Result.error("编码已存在!");
        }
        return Result.success();
    }

    /**
     * 在更新之前,要先判断sn之前是否在数据库中存在,排除自己
     *
     * @param sn
     * @return
     */
    public Result getFunctionBySnExclude(String sn, Long id) {

        if (this.getOne(new QueryWrapper<AiFunctionResource>().eq("sn", sn).ne("id", id)) != null) {
            return Result.error("编码已存在!");
        }
        return Result.success();
    }


    @Transactional
    public Result del(Long id) {
        AiFunctionResource resource = this.getById(id);
        if (resource.getEnable()) {
            Result result = unRegisterResource(resource);
            if (result.isError()) {
                throw new RuntimeException("资源注销失败");
            }
        }
        if (removeById(id)) {
            //移除绑定关系,去找到appConfig配置表中相关的functioncall要清理掉
            Result result = aiAppConfigService.delFuncConfigByFuncId(id);
            if (result.isError()) {
                throw new RuntimeException("appConfig配置表清理失败");
            }
            return Result.success();
        } else {
            return Result.error("删除失败");
        }


    }


    @Override
    @Transactional
    public Result toggleEnable(Long id) {
        Result result = getFunctionById(id);
        if (result.isError()) {
            return result;
        }
        AiFunctionResource function = (AiFunctionResource) result.getData();
        boolean enable = !function.getEnable();
        boolean update = this.update(new UpdateWrapper<AiFunctionResource>().eq("id", id).set("enable", enable));
        if (update) {
            if (enable) {
                Result registerResult = registerResource(function);
                if (registerResult.isError()) {
                    throw new RuntimeException("资源注册失败");
                }
                return Result.success();
            } else {
                Result unRegisterResult = unRegisterResource(function);
                if (unRegisterResult.isError()) {
                    throw new RuntimeException("资源注销失败");
                }
                return Result.success(update);
            }

        } else {
            return Result.error("更新启用状态失败");
        }
    }


    @Override
    public List<AiFunctionResource> getFunctionResourceList(String[] functions) {
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.in("id", functions);
        return this.list(queryWrapper);

    }

    /**
     * 注册资源
     *
     * @return
     */
    public Result registerResource(AiFunctionResource function) {
        logger.info("function资源--{}--注册资源", function.getSn());
        try {
            Result<List<FunctionResource>> result = getJblotFunctionList(Arrays.asList(function.getId().toString()));
            FunctionResource resource = result.getData().get(0);
            ResourceCenter.registerFunction(resource);
            //在吧这个对象放到缓存中
            List<AiFunctionParam> paramList = aiFunctionParamService.getParamListByFunctionId(function.getId());
            if (paramList != null && paramList.size() > 0) {
                Method method = NativeFunctionCall.getMethod(function.getServiceClass(), function.getServiceMethod(), paramList);
                String key = function.getServiceClass() + "#" + function.getServiceMethod();
                NativeFunctionCall.cacheMethod(key, method);
            }
            return Result.success();
        } catch (Exception e) {
            logger.error("function资源--{}--注册资源失败,异常原因:{}", function.getSn(), e.getMessage());
            return Result.error("注册失败");
        }
    }

    /**
     * 移除资源
     *
     * @param function
     * @return
     */
    public Result unRegisterResource(AiFunctionResource function) {
        logger.info("function资源--{}--移除资源", function.getSn());
        try {
            ResourceCenter.unregisterFunction(function.getId().toString());
            //从缓存中移除
            String key = function.getServiceClass() + "#" + function.getServiceMethod();
            NativeFunctionCall.removeMethod(key);
            return Result.success();
        } catch (Exception e) {
            logger.error("function资源--{}--移除资源失败,异常原因:{}", function.getSn(), e.getMessage());
            return Result.error("移除失败");
        }
    }

    /**
     * 注册所有资源,是在项目启动时候调用
     */
    public void registerAllResource() {
        //只启动开启状态的资源
        this.list(new QueryWrapper<AiFunctionResource>().eq("enable", true)).
                forEach(this::registerResource);
    }


    /**
     * 根据function的id集合,返回jboltai的function集合
     *
     * @param idList
     * @return
     */
    public Result<List<FunctionResource>> getJblotFunctionList(List<String> idList) {
        List<AiFunctionResource> dbFunctionList = this.list(new QueryWrapper<AiFunctionResource>().in("id", idList));
        List<AiFunctionParam> dbParamList = aiFunctionParamService.getParamListByFunctionId(idList);
        Map<Long, List<AiFunctionParam>> dbParamMap =
                dbParamList.stream()
                        //分组
                        .collect(Collectors.groupingBy(AiFunctionParam::getAiFunctionId)).entrySet().stream()
                        // 对每个分组的列表按照getSortRank属性进行排序
                        .collect(Collectors.toMap(Map.Entry::getKey,
                                entry -> entry.getValue().stream()
                                        .sorted(Comparator.comparing(AiFunctionParam::getSortRank))
                                        .collect(Collectors.toList())));

        List<FunctionResource> resourcesList = new ArrayList<>();
        for (AiFunctionResource db : dbFunctionList) {
            FunctionResource functionResource = new FunctionResource(db.getId().toString(), db.getSn(), db.getBriefInfo());
            List<AiFunctionParam> dbParamListByFId = dbParamMap.get(db.getId());
            if (dbParamListByFId != null) {
                List<FunctionParam> paramList = new ArrayList<>();
                for (AiFunctionParam dbParam : dbParamListByFId) {
                    FunctionParam param = new FunctionParam(dbParam.getParamName(), dbParam.getType(), dbParam.getRequired(), dbParam.getDefaultValue(), dbParam.getBriefInfo());
                    paramList.add(param);
                }
                functionResource.setParams(paramList);
            }
            resourcesList.add(functionResource);
        }
        return Result.success(resourcesList);
    }


    static class FunctionResourceWrapper {
        AiFunctionResource resource;
        Method method;

        public FunctionResourceWrapper(AiFunctionResource resource, Method method) {
            this.resource = resource;
            this.method = method;
        }
    }


    /**
     * 同步本地Function资源到数据库
     *
     * @param strategyCode 同步策略
     * @return 同步结果
     */
    @Transactional(rollbackFor = Exception.class)
    public Result syncNativeFunctions(String strategyCode) {
        try {
            // 1. 扫描所有带有FunctionResource注解的方法
            Set<Method> annotatedMethods = scanAnnotatedMethods();

            // 2. 解析方法生成AiFunctionResource对象
            List<FunctionResourceWrapper> localFunctions = annotatedMethods.stream()
                    .map(this::convertMethodToFunction)
                    .collect(Collectors.toList());
            // 3. 同步到数据库
            SyncStrategy strategy = SyncStrategy.fromCode(strategyCode);
            return syncFunctionsToDatabase(localFunctions, strategy);
        } catch (Exception e) {
            logger.error("同步Native Functions失败", e);
            return Result.error("同步失败：" + e.getMessage());
        }
    }

    /**
     * 扫描所有带有FunctionResource注解的方法
     *
     * @return 带有注解的方法集合
     */
    private Set<Method> scanAnnotatedMethods() {
        Set<Method> annotatedMethods = new HashSet<>();
        try {
            Resource[] resources = resourcePatternResolver.getResources("classpath*:**/*.class");
            for (Resource resource : resources) {
                processResource(resource, annotatedMethods);
            }
        } catch (IOException e) {
            logger.error("扫描类文件失败", e);
        }
        return annotatedMethods;
    }

    /**
     * 处理单个资源文件
     *
     * @param resource         资源文件
     * @param annotatedMethods 注解方法集合
     */
    private void processResource(Resource resource, Set<Method> annotatedMethods) {
        try {
            String className = resource.getURL().getPath();
            if (!className.endsWith(".class")) {
                return;
            }
            className = className.substring(className.indexOf("/classes/") + 9, className.length() - 6)
                    .replace('/', '.');

            try {
                Class<?> clazz = Class.forName(className);
                for (Method method : clazz.getDeclaredMethods()) {
                    if (method.isAnnotationPresent(cn.jbolt.ai.resource.function.anno.FunctionResource.class)) {
                        annotatedMethods.add(method);
                    }
                }
            } catch (Throwable e) {
                logger.debug("Failed to load class: " + className, e);
            }
        } catch (Exception e) {
            logger.debug("Failed to process resource: " + resource, e);
        }
    }

    /**
     * 将方法转换为AiFunctionResource对象
     *
     * @param method 方法
     * @return AiFunctionResource对象
     */
    private FunctionResourceWrapper convertMethodToFunction(Method method) {
        cn.jbolt.ai.resource.function.anno.FunctionResource annotation =
                method.getAnnotation(cn.jbolt.ai.resource.function.anno.FunctionResource.class);

        AiFunctionResource function = new AiFunctionResource();
        function.setType("Native");
        function.setServiceClass(method.getDeclaringClass().getName());
        function.setServiceMethod(method.getName());

        // 设置sn
        String sn = generateSn(method, annotation.sn());
        function.setSn(sn);

        function.setBriefInfo(annotation.desc());
        function.setParamCount(method.getParameterCount());
        function.setEnable(false);

        return new FunctionResourceWrapper(function, method);
    }

    /**
     * 生成方法的SN
     *
     * @param method       方法
     * @param annotationSn 注解中指定的SN
     * @return 生成的SN
     */
    private String generateSn(Method method, String annotationSn) {
        if (StrUtil.isNotBlank(annotationSn)) {
            return annotationSn;
        }

        // 使用包名首字母+方法名生成sn
        String[] packageParts = method.getDeclaringClass().getName().split("\\.");
        StringBuilder snBuilder = new StringBuilder();
        for (String part : packageParts) {
            if (!part.isEmpty()) {
                snBuilder.append(part.charAt(0));
            }
        }
        snBuilder.append(".").append(method.getName());
        return snBuilder.toString();
    }

    /**
     * 同步函数到数据库
     *
     * @param localFunctions 本地函数列表
     * @param strategy       同步策略
     * @return 同步结果
     */
    private Result syncFunctionsToDatabase(List<FunctionResourceWrapper> localFunctions, SyncStrategy strategy) {
        try {
            // 获取数据库中现有的函数
            List<AiFunctionResource> dbFunctions = this.list(new QueryWrapper<AiFunctionResource>()
                    .eq("type", "Native"));

            Map<String, AiFunctionResource> dbFunctionMap = dbFunctions.stream()
                    .collect(Collectors.toMap(
                            f -> f.getServiceClass() + "." + f.getServiceMethod(),
                            f -> f
                    ));

            // 处理本地扫描到的函数
            for (FunctionResourceWrapper item : localFunctions) {
                AiFunctionResource localFunction = item.resource;
                String key = localFunction.getServiceClass() + "." + localFunction.getServiceMethod();
                AiFunctionResource dbFunction = dbFunctionMap.get(key);

                if (dbFunction == null) {
                    // 新增
                    this.add(localFunction);
                } else {
                    // 更新
                    switch (strategy) {
                        case FULL:
                            // 全量更新
                            localFunction.setId(dbFunction.getId());
                            localFunction.setEnable(dbFunction.getEnable());
                            this.updateById(localFunction);
                            break;
                        case INCREMENT:
                            // 仅更新参数数量
                            localFunction.setId(dbFunction.getId());
                            dbFunction.setParamCount(localFunction.getParamCount());
                            this.updateById(dbFunction);
                            break;
                    }
                    // 从Map中移除已处理的函数
                    dbFunctionMap.remove(key);
                }
                List<AiFunctionParam> params = convertMethodParamsToFunctionParams(item.method, localFunction.getId());
                aiFunctionParamService.syncNativeFunctionParams(localFunction.getId(), params, strategy);

            }

            // 处理需要删除的函数
            if (!dbFunctionMap.isEmpty()) {
                List<Long> toDeleteIds = dbFunctionMap.values().stream()
                        .map(AiFunctionResource::getId)
                        .collect(Collectors.toList());
                for (Long id : toDeleteIds) {
                    this.del(id);
                }
            }

            return Result.success("同步完成");
        } catch (Exception e) {
            logger.error("同步到数据库失败", e);
            return Result.error("同步失败：" + e.getMessage());
        }
    }

    private List<AiFunctionParam> convertMethodParamsToFunctionParams(Method method, Long aiFunctionId) {
        List<AiFunctionParam> params = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            if (parameter.getType() == AIFuncCallEvent.class
                    || WebSocketSession.class.isAssignableFrom(parameter.getType())
                    || ChainContext.class == parameter.getType()
            ) {
                //事件参数不作为参数定义
                continue;
            }
            AiFunctionParam param = new AiFunctionParam();
            param.setAiFunctionId(aiFunctionId);
            param.setParamName(parameter.getName());
            param.setType(parameter.getType().getName());
            param.setSortRank(i);

            // 处理@FunctionParam注解
            cn.jbolt.ai.resource.function.anno.FunctionParam annotation = parameter.getAnnotation(cn.jbolt.ai.resource.function.anno.FunctionParam.class);
            if (annotation != null) {
                param.setBriefInfo(annotation.description());
                param.setRequired(annotation.required());
                param.setDefaultValue(annotation.defaultValue());
            } else {
                // 默认值
                param.setRequired(true);
                param.setBriefInfo("");
                param.setDefaultValue("");
            }
            params.add(param);
        }
        return params;
    }

    @Override
    public Result syncStrategyList() {
        return Result.success(SyncStrategy.list());
    }

    public Result updateFunctionParamCount(Long aiFunctionId, int increment) {
        boolean update = this.update(new UpdateWrapper<AiFunctionResource>().eq("id", aiFunctionId).setSql("param_count = param_count + " + increment));
        if (update) {
            return Result.success();
        } else {
            return Result.error("更新失败");
        }
    }

    /**
     * 检查函数的状态,一般在操作参数前判断
     *
     * @return
     */
    public Result checkFunctionStatus(Long id) {
        AiFunctionResource function = this.getById(id);
        if (function == null) {
            return Result.error("AI函数不存在");
        }
        if (function.getEnable()) {
            return Result.error("请关闭function资源,在进行参数的操作");
        }
        return Result.success();
    }
}
